#ifndef _OBSTACLE_H_
#define _OBSTACLE_H_

#include <memory>
#include "player.hpp"
#include "protocol.hpp"

namespace Server
{
	enum
	{
		OBSTACLE_DEFAULT_LIFE = 10,
		BONUS_LIFETIME = 200,
		BONUS_WORKING_TIME = 50,
		CHARGE_STATION_GAIN = 10,
		VISUAL_SECTOR_BONUS_GAIN = 4,
		SHOOT_SECTOR_BONUS_GAIN = 4,
		MAX_ENERGY_BONUS_GAIN = 10,
		MAX_VELOCITY_BONUS_GAIN = 1,
		MAX_FIRE_BONUS_GAIN = 3
	};

	// Fat interface to all objects
	class Object
	{
		public:
			virtual Object *enter(Player &) { return this; }
			virtual Object *timeStep(Player &) { return this; };
			virtual Object *leave(Player &) { return this;}
			virtual Object *damage(int) { return this; }
			virtual unsigned char objectId() const = 0;
			virtual Object *clone() const = 0;
			static void del(Object *obj) { delete obj; }
			virtual ~Object() = 0;
	};

	class Obstacle : public Object
	{
		int life_;

	protected:
		Obstacle(int life = OBSTACLE_DEFAULT_LIFE) : life_(life) {}

	public:
		Obstacle *damage(int power);
	};
	
	class Forest : public Obstacle {
		unsigned char objectId() const;
		Forest *clone() const;
	};

	class Bridge : public Obstacle
	{
	public:
		Obstacle *damage(int power);
		unsigned char objectId() const;
		Bridge *clone() const;
	};

	class River : public Obstacle
	{
	public:
		River *enter(Player &player);
		unsigned char objectId() const;
		River *clone() const;
	};

	class Swamp : public Obstacle
	{
	public:
		Swamp *enter(Player &player);
		Swamp *leave(Player &player);
		unsigned char objectId() const;
		Swamp *clone() const;
	};

	class Bonus : public Object
	{
	public:
	protected:
		Bonus(int time) :
			time_(time)
		{}

		static void scheduleCancel(int time, const Player &player, short Player::*property, short value);
		int time_;	
		
	private:
		static void cancel(int playerId, short Player::*property, short value);
	};
	
	// ???
	template <short Player::*property, int gain, int maxProperty = 1024>
	class BonusImpl : public Bonus
	{
	public:
		BonusImpl(int time = BONUS_WORKING_TIME) :
			Bonus(time)
		{}

		BonusImpl *enter(Player &player)
		{
			player.*property += gain;
			if (player.*property > maxProperty)
				player.*property = maxProperty;
			Bonus::scheduleCancel(time_, player, property, gain);
			delete this;
			return 0;
		}

		unsigned char objectId() const { return Proto::FIELD_BONUS; }

		BonusImpl *clone() const
		{
			return new BonusImpl(*this);
		}

	};

	typedef BonusImpl<&Player::visualSector, VISUAL_SECTOR_BONUS_GAIN> VisualSectorBonus;
	typedef BonusImpl<&Player::shootSector, SHOOT_SECTOR_BONUS_GAIN> ShootSectorBonus;
	typedef BonusImpl<&Player::maxEnergy, MAX_ENERGY_BONUS_GAIN> MaxEnergyBonus;
	typedef BonusImpl<&Player::maxFire, MAX_FIRE_BONUS_GAIN> MaxFireBonus;
	typedef BonusImpl<&Player::maxVelocity, MAX_VELOCITY_BONUS_GAIN> MaxVelocityBonus;

	class ChargeStation : public Object
	{
	public:
		ChargeStation *timeStep(Player &player);
		unsigned char objectId() const;
		ChargeStation *clone() const;
	};

	class ObjectHolder
	{
		Object *obj_;
		int objectNumber_;

	public:
		ObjectHolder() : obj_(0), objectNumber_(0) {}
		~ObjectHolder() { delete obj_; }

		void setObject(Object *obj);

		// delegate all Object's methods
		void enter(Player &player);
		void leave(Player &player);
		void timeStep(Player &player);
		void damage(int value);
		unsigned char objectId() const;
		operator const void*() const {return obj_;}
		int getObjectNumber() const { return objectNumber_; }

	protected:
		ObjectHolder(const ObjectHolder&);
		void operator =(const ObjectHolder&);
	};
}

#endif
